package me.itsoo.artemis.framework.repository.mapper;

import lombok.Getter;
import me.itsoo.artemis.framework.core.tuple.Pair;
import me.itsoo.artemis.framework.core.tuple.SimplePair;
import me.itsoo.artemis.framework.core.util.AnnotationUtils;
import me.itsoo.artemis.framework.core.util.StringUtils;
import me.itsoo.artemis.framework.repository.lang.Field;
import me.itsoo.artemis.framework.repository.lang.PrimaryKey;
import me.itsoo.artemis.framework.repository.lang.Table;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * MapperDefinition
 *
 * @author zxy
 * @version 0.0.1
 * @since 2023/5/11
 */
public class MapperDefinition {

    private final java.lang.Class<?> sourceType;

    private final java.lang.reflect.Field[] fields;

    @Getter
    private String tableName;

    @Getter
    private final List<Pair<String, java.lang.reflect.Field>> fieldNames;

    @Getter
    private final List<Pair<String, java.lang.reflect.Field>> primaryKeys;

    private MapperDefinition(Class<?> sourceType) {
        this.sourceType = sourceType;
        this.fields = Objects.isNull(sourceType) ? new java.lang.reflect.Field[0] : sourceType.getDeclaredFields();

        this.tableName = computeThisTableName();
        this.fieldNames = computeThisFieldNames();
        this.primaryKeys = computeThisPrimaryKeys();
    }

    /**
     * 创建实例
     *
     * @param type Class
     * @return MapperDefinition
     */
    public static MapperDefinition create(Class<?> type) {
        return Objects.nonNull(type) ? new MapperDefinition(type) : null;
    }

    /**
     * 重置 tableName
     *
     * @param tableName String
     */
    public void setTableName(String tableName) {
        this.tableName = tableName;
    }

    /**
     * 是给定的类型
     *
     * @param type Class
     * @return boolean
     */
    public boolean isAssignableFrom(Class<?> type) {
        return this.sourceType.isAssignableFrom(type);
    }

    /**
     * 计算表名称
     *
     * @return String
     */
    private String computeThisTableName() {
        final Table tAnn = AnnotationUtils.findAnnotation(sourceType, Table.class);
        if (Objects.nonNull(tAnn)) {
            final String value = tAnn.value();
            return StringUtils.defaultIfBlank(value, sourceType.getSimpleName());
        }

        return null;
    }

    /**
     * 计算字段属性（含主键）
     *
     * @return List
     */
    private List<Pair<String, java.lang.reflect.Field>> computeThisFieldNames() {
        final List<Pair<String, java.lang.reflect.Field>> result = new ArrayList<>(fields.length);
        for (java.lang.reflect.Field df : fields) {
            final Field fAnn = AnnotationUtils.findAnnotation(df, Field.class);
            final String fieldName = Optional.ofNullable(fAnn).map(Field::value).orElse(null);
            final String k = StringUtils.defaultIfBlank(fieldName, df.getName());
            result.add(SimplePair.create(k, df));
        }

        return result;
    }

    /**
     * 计算主键属性
     *
     * @return List
     */
    private List<Pair<String, java.lang.reflect.Field>> computeThisPrimaryKeys() {
        final List<Pair<String, java.lang.reflect.Field>> result = new ArrayList<>(fields.length);
        for (java.lang.reflect.Field df : fields) {
            final PrimaryKey pkAnn = AnnotationUtils.findAnnotation(df, PrimaryKey.class);
            if (Objects.nonNull(pkAnn)) {
                final String k = StringUtils.defaultIfBlank(pkAnn.value(), df.getName());
                result.add(SimplePair.create(k, df));
            }
        }

        return result;
    }
}
